package org.example.service.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.WebSocket;
import org.example.commonapi.domain.Player;
import org.example.commonapi.domain.R;
import org.example.commonapi.domain.UserTalkInfo;
import org.example.commonapi.request.aippt.ApiClient;
import org.example.commonapi.request.characterSimulation.AgentRequest;
import org.example.commonapi.request.characterSimulation.InteractiveRequest;
import org.example.commonapi.request.characterSimulation.PlayerRequest;
import org.example.commonapi.request.fileUnderstand.ChatRequest;
import org.example.commonapi.response.RoleContent;
import org.example.commonapi.response.aippt.CreateResponse;
import org.example.commonapi.response.aippt.ProgressResponse;
import org.example.commonapi.response.aippt.ThemeInfo;
import org.example.commonapi.response.bigmodel.SparkChatListener;
import org.example.commonapi.response.characterSimulation.CharacterSimulationListener;
import org.example.commonapi.response.fileUnderstand.FileStatusResponse;
import org.example.commonapi.response.fileUnderstand.FileUnderstandListener;
import org.example.commonapi.response.fileUnderstand.UploadResponse;
import org.example.commonapi.response.speechSynthesis.SpeechSynthesisListener;
import org.example.commonapi.util.JwtUtil;
import org.example.commonapi.util.redis.AiPrefix;
import org.example.commonapi.util.redis.RedisService;
import org.example.service.AiChatService;
import org.example.service.PlayerService;
import org.example.utils.HttpUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static org.example.utils.AuthUtil.getAuthUrl;
import static org.example.utils.AuthUtil.getSignature;

@Service
@Slf4j
public class AiChatServiceImpl implements AiChatService {
    @Value("${APP.APPID}")
    public String appId;
    @Value("${APP.APISecret}")
    public String apiSecret;
    @Value("${APP.APIKey}")
    public String apiKey;
    // 用户和ai对话的历史记录
    private Map<UserTalkInfo,List<RoleContent>> userTalkHistoryMap = new HashMap<>();
    // 用来缓存 用户的SseEmitter
    private static final Map<Object, SseEmitter> SSE_CACHE = new ConcurrentHashMap<>();
    // 合成文本编码格式
    public static final String TTE = "UTF8"; // 小语种必须使用UNICODE编码作为值
    // 发音人参数。到控制台-我的应用-语音合成-添加试用或购买发音人，添加后即显示该发音人参数值，若试用未添加的发音人会报错11200
    public static final String VCN = "xiaoyan";

    @Autowired
    private PlayerService playerService;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private RedisService redisService;

    @Override
    public SparkChatListener chat(String question, StringBuilder answer,Long userId) throws Exception {
        UserTalkInfo userTalkInfo = new UserTalkInfo(userId, "chat");
        List<RoleContent> talkHistory;
        if (userTalkHistoryMap.get(userTalkInfo) == null) {
            talkHistory = new ArrayList<>();
            userTalkHistoryMap.put(userTalkInfo,talkHistory);
        }else {
            talkHistory = userTalkHistoryMap.get(userTalkInfo);
        }
        // 将用户本次问问题加入历史记录中
        RoleContent roleContent = new RoleContent("user",question,"text");
        talkHistory.add(roleContent);
        String hostUrl = "https://spark-api.xf-yun.com/v3.5/chat";
        // 构建鉴权url
        String authUrl = getAuthUrl(hostUrl,apiKey,apiSecret,"GET");
        OkHttpClient client = new OkHttpClient.Builder().build();
        // 构建聊天请求
        JSONObject chatRequest = buildChatRequest(question,talkHistory);
        // 构建websocket请求
        Request request = new Request.Builder().url(authUrl).build();
        SparkChatListener sparkChatListener = new SparkChatListener(answer,talkHistory);
        // 发起websocket请求
        WebSocket webSocket = client.newWebSocket(request, sparkChatListener);
        if (chatRequest != null) {
            webSocket.send(chatRequest.toJSONString());
        }
        return sparkChatListener;
    }

    @Override
    public SseEmitter getConn(long key) {
        final SseEmitter sseEmitter = SSE_CACHE.get(key);
        if (sseEmitter != null) {
            return sseEmitter;
        } else {
            // 设置连接超时时间，需要配合配置项 spring.mvc.async.request-timeout: 600000 一起使用
            final SseEmitter emitter = new SseEmitter(600000L);
            // 注册超时回调，超时后触发
            emitter.onTimeout(() -> {
                log.info("连接已超时，正准备关闭，key = {}", key);
                SSE_CACHE.remove(key);
            });
            // 注册完成回调，调用 emitter.complete() 触发
            emitter.onCompletion(() -> {
                log.info("连接已关闭，正准备释放，key = {}", key);
                SSE_CACHE.remove(key);
                log.info("连接已释放，key = {}", key);
            });
            // 注册异常回调，调用 emitter.completeWithError() 触发
            emitter.onError(throwable -> {
                log.error("连接已异常，正准备关闭，key = {}", key, throwable);
                SSE_CACHE.remove(key);
            });
            System.out.println("放入Key=>" + key);
            SSE_CACHE.put(key, emitter);
            return emitter;
        }
    }

    @Override
    public R<String> draw(String question, Integer width, Integer height) throws Exception {
        String hostUrl = "https://spark-api.cn-huabei-1.xf-yun.com/v2.1/tti";
        String authUrl = getAuthUrl(hostUrl, apiKey, apiSecret,"POST");
        // URL地址正确
        String json = "{\n" +
                "  \"header\": {\n" +
                "    \"app_id\": \"" + appId + "\",\n" +
                "    \"uid\": \"" + UUID.randomUUID().toString().substring(0, 15) + "\"\n" +
                "  },\n" +
                "  \"parameter\": {\n" +
                "    \"chat\": {\n" +
                "      \"domain\": \"s291394db\",\n" +
                "      \"temperature\": 0.5,\n" +
                "      \"max_tokens\": 4096,\n" +
                "      \"width\": " + width + ",\n" +
                "      \"height\": " + height + "\n" +
                "    }\n" +
                "  },\n" +
                "  \"payload\": {\n" +
                "    \"message\": {\n" +
                "      \"text\": [\n" +
                "        {\n" +
                "          \"role\": \"user\",\n" +
                "          \"content\": \"" + question + "\"\n" +
                "        }\n" +
                "      ]\n" +
                "    }\n" +
                "  }\n" +
                "}";
        // 发起Post请求
        System.err.println(json);
        String res = HttpUtil.doPostJsonMapParams(authUrl, null, json);
        System.out.println(res);
        // 转化为json字符串
        JSONObject jsonObject = JSON.parseObject(res);
        System.out.println(res);
        // 获取Payload
        JSONObject payload = jsonObject.getJSONObject("payload");
        // 获取Message
        JSONObject choices = payload.getJSONObject("choices");
        // 获取Text
        JSONArray text = choices.getJSONArray("text");
        // 获取Text中的内容
        String content = text.getJSONObject(0).getString("content");
        System.out.println(content);
        return R.success(content);
    }

    @Override
    public R<String> judgePlayerReigster(String playerName) throws Exception {
        String url = "https://ai-character.xfyun.cn/api/open/player/if-register?appId=" + appId + "&playerName=" + playerName;
        System.out.println("url:" + url);
        long ts = System.currentTimeMillis();
        Request request = new Request.Builder()
                .url(url)
                .get()
                .addHeader("appId", appId)
                .addHeader("timestamp", String.valueOf(ts))
                .addHeader("signature", Objects.requireNonNull(getSignature(appId, apiSecret, ts)))
                .build();
        OkHttpClient client = new OkHttpClient();
        Response response = client.newCall(request).execute();
        R<String> responseMsg = JSONObject.parseObject(response.body().string(), new TypeReference<R<String>>() {
        });
        System.out.println(responseMsg);
        if (responseMsg.getCode() != 10000) {
            return R.error("玩家名称已存在!");
        }
        return R.success(responseMsg.getData());
    }

    @Override
    public R<String> playerRegister(Player player,HttpServletRequest httpServletRequest) throws Exception {
        String url = "https://ai-character.xfyun.cn/api/open/player/register";
        System.out.println("url:" + url);
        PlayerRequest playerRequest = new PlayerRequest();
        BeanUtils.copyProperties(player, playerRequest);
        playerRequest.setAppId(appId);
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),JSON.toJSONString(playerRequest));
        long ts = System.currentTimeMillis();
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .addHeader("appId", appId)
                .addHeader("timestamp", String.valueOf(ts))
                .addHeader("signature", Objects.requireNonNull(getSignature(appId, apiSecret, ts)))
                .build();
        OkHttpClient client = new OkHttpClient();
        Response response = client.newCall(request).execute();
        R<String> responseMsg = JSONObject.parseObject(response.body().string(), new TypeReference<R<String>>() {
        });
        System.out.println(responseMsg);
        if (responseMsg.getCode() != 10000) {
            return R.error("玩家注册失败!");
        }
        // 设置player的playerID
        Long creatorId = jwtUtil.parseJwt(httpServletRequest.getHeader("token")).getId();
        player.setCreatorId(creatorId);
        player.setPlayerId(responseMsg.getData());
        playerService.save(player);
        return R.success(responseMsg.getData());
    }

    @Override
    public R<String> playerEdit(Player player) throws Exception {
        String url = "https://ai-character.xfyun.cn/api/open/player/modify";
        System.out.println("url:" + url);
        PlayerRequest playerRequest = new PlayerRequest();
        BeanUtils.copyProperties(player, playerRequest);
        playerRequest.setAppId(appId);
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JSON.toJSONString(playerRequest));
        long ts = System.currentTimeMillis();
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .addHeader("appId", appId)
                .addHeader("timestamp", String.valueOf(ts))
                .addHeader("signature", Objects.requireNonNull(getSignature(appId, apiSecret, ts)))
                .build();
        OkHttpClient client = new OkHttpClient();
        Response response = client.newCall(request).execute();
        R<String> responseMsg = JSONObject.parseObject(response.body().string(), new TypeReference<R<String>>() {
        });
        System.out.println(responseMsg);
        if (responseMsg.getCode() != 10000) {
            throw new Exception("编辑玩家失败，responseMsg =" + responseMsg);
        }
        LambdaQueryWrapper<Player> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Player::getPlayerId, player.getPlayerId());
        playerService.update(player, queryWrapper);
        return R.success();
    }

    @Override
    public R<String> playerDelete(Player player) throws Exception {
        String url = "https://ai-character.xfyun.cn/api/open/player/delete";
        System.out.println("url:" + url);
        PlayerRequest playerRequest = new PlayerRequest();
        BeanUtils.copyProperties(player, playerRequest);
        playerRequest.setAppId(appId);
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JSON.toJSONString(playerRequest));
        long ts = System.currentTimeMillis();
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .addHeader("appId", appId)
                .addHeader("timestamp", String.valueOf(ts))
                .addHeader("signature", Objects.requireNonNull(getSignature(appId, apiSecret, ts)))
                .build();
        OkHttpClient client = new OkHttpClient();
        Response response = client.newCall(request).execute();
        R<String> responseMsg = JSONObject.parseObject(response.body().string(), new TypeReference<R<String>>() {
        });
        System.out.println(responseMsg);
        if (responseMsg.getCode() != 10000) {
            throw new Exception("编辑玩家失败，responseMsg =" + responseMsg);
        }
        LambdaQueryWrapper<Player> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Player::getPlayerId, player.getPlayerId());
        playerService.remove(queryWrapper);
        return R.success();
    }

    @Override
    public R<String> agentCreate(AgentRequest agentRequest) throws Exception {
        String url = "https://ai-character.xfyun.cn/api/open/agent/edit-character";
        System.out.println("url:" + url);
        agentRequest.setAppId(appId);
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JSONObject.toJSONString(agentRequest));
        long ts = System.currentTimeMillis();
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .addHeader("appId",appId)
                .addHeader("timestamp", String.valueOf(ts))
                .addHeader("signature", Objects.requireNonNull(getSignature(appId, apiSecret, ts)))
                .build();
        OkHttpClient client = new OkHttpClient();
        Response response = client.newCall(request).execute();
        R<String> responseMsg = JSONObject.parseObject(response.body().string(), new TypeReference<R<String>>() {
        });
        System.out.println(agentRequest);
        System.out.println(responseMsg);
        if (responseMsg.getCode() != 10000){
            throw new Exception("创建人格失败，responseMsg =" + responseMsg);
        }
        // 将玩家id和人格id及人格名称存入redis
        redisService.set(AiPrefix.USER_AGENT,agentRequest.getPlayerId(),responseMsg.getData() + "/" + agentRequest.getAgentName(),36000000);
        return R.success(responseMsg.getData());
    }

    @Override
    public R<String> agentEdit(AgentRequest agentRequest) throws Exception {
        String url = "https://ai-character.xfyun.cn/api/open/agent/edit-character";
        System.out.println("url:" + url);
        agentRequest.setAppId(appId);
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JSONObject.toJSONString(agentRequest));
        long ts = System.currentTimeMillis();
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .addHeader("appId",appId)
                .addHeader("timestamp", String.valueOf(ts))
                .addHeader("signature", Objects.requireNonNull(getSignature(appId, apiSecret, ts)))
                .build();
        OkHttpClient client = new OkHttpClient();
        Response response = client.newCall(request).execute();
        R<String> responseMsg = JSONObject.parseObject(response.body().string(), new TypeReference<R<String>>() {
        });
        System.out.println(responseMsg);
        if (responseMsg.getCode() != 10000){
            throw new Exception("编辑人格失败，responseMsg =" + responseMsg);
        }
        return R.success();
    }

    @Override
    public R<AgentRequest> getAgentInfo(String playerId) throws Exception {
        String url =  "https://ai-character.xfyun.cn/api/open/agent/get-character";
        StringBuilder sb = new StringBuilder();
        if ( redisService.get(AiPrefix.USER_AGENT, playerId) == null ) {
            return R.error("请先创建人格!");
        }
        String agentInfo = redisService.get(AiPrefix.USER_AGENT, playerId).toString();
        String agentId = agentInfo.split("/")[0];
        String agentName = agentInfo.split("/")[1];
        System.out.println(agentInfo);
        sb.append(url).append("?appId=").append(appId).append("&agentId=").append(agentId).append("&agentName=").append(agentName);
        System.out.println("url:" + sb);
        long ts = System.currentTimeMillis();
        Request request = new Request.Builder()
                .url(sb.toString())
                .addHeader("appId",appId)
                .addHeader("timestamp", String.valueOf(ts))
                .addHeader("signature", Objects.requireNonNull(getSignature(appId, apiSecret, ts)))
                .build();
        OkHttpClient client = new OkHttpClient();
        Response response = client.newCall(request).execute();
        R<AgentRequest> responseMsg = JSONObject.parseObject(response.body().string(), new TypeReference<R<AgentRequest>>() {
        });
        System.out.println(responseMsg);
        if (responseMsg.getCode() != 10000){
            throw new Exception("获取人格失败，responseMsg =" + responseMsg);
        }
        return R.success(responseMsg.getData());
    }

    @Override
    public R<String> agentDelete(AgentRequest agentRequest) throws Exception {
        String url = "https://ai-character.xfyun.cn/api/open/agent/delete-character";
        StringBuilder sb = new StringBuilder();
        sb.append(url).append("?appId=").append(appId).append("&agentId=").append(agentRequest.getAgentId());
        System.out.println("url:" + sb);
        long ts = System.currentTimeMillis();
        Request request = new Request.Builder()
                .url(sb.toString())
                .delete()
                .addHeader("appId",appId)
                .addHeader("timestamp", String.valueOf(ts))
                .addHeader("signature", Objects.requireNonNull(getSignature(appId, apiSecret, ts)))
                .build();
        OkHttpClient client = new OkHttpClient();
        Response response = client.newCall(request).execute();
        R<Boolean> responseMsg = JSONObject.parseObject(response.body().string(), new TypeReference<R<Boolean>>() {
        });
        System.out.println(responseMsg);
        if (responseMsg.getCode() != 10000){
            throw new Exception("删除人格失败，responseMsg =" + responseMsg);
        }
        return R.success();
    }

    @Override
    public R<String> generateMemory(InteractiveRequest interactiveRequest) throws Exception {
        String url = "https://ai-character.xfyun.cn/api/open/interactive/generate";
        System.out.println("url:" + url);
        interactiveRequest.setAppId(appId);
        interactiveRequest.setInteractionType("obs");
        System.out.println(interactiveRequest);
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JSON.toJSONString(interactiveRequest));
        System.out.println(requestBody);
        long ts = System.currentTimeMillis();
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .addHeader("appId", appId)
                .addHeader("timestamp", String.valueOf(ts))
                .addHeader("signature", Objects.requireNonNull(getSignature(appId, apiSecret, ts)))
                .build();
        OkHttpClient client = new OkHttpClient();
        Response response = client.newCall(request).execute();
        R<String> responseMsg = JSONObject.parseObject(response.body().string(), new TypeReference<R<String>>() {
        });
        System.out.println(responseMsg);
        if (responseMsg.getCode() != 10000) {
            throw new Exception("生成记忆失败，responseMsg =" + responseMsg);
        }
        return R.success();
    }

    @Override
    public CharacterSimulationListener characterSimulationChat(String question, StringBuilder answer,String playerId,String agentId,String chatId,String preChatId) {
        String url = "wss://ai-character.xfyun.cn/api/open/interactivews/";
        // 构造url鉴权
        long ts = System.currentTimeMillis();
        String signature = getSignature(appId, apiSecret, ts);
        String requestUrl = url + chatId + "?" + "appId=" + appId + "&timestamp=" + ts + "&signature=" + signature;
        OkHttpClient client = new OkHttpClient.Builder().build();
        // 构建聊天请求
        JSONObject chatRequest = buildCharacterSimulationRequest(question, playerId, agentId,chatId, preChatId);
        // 构建websocket请求
        Request request = new Request.Builder().url(requestUrl).build();
        CharacterSimulationListener characterSimulationListener = new CharacterSimulationListener(answer);
        // 发起websocket请求
        WebSocket webSocket = client.newWebSocket(request, characterSimulationListener);
        if (chatRequest != null) {
            webSocket.send(chatRequest.toJSONString());
        }
        return characterSimulationListener;
    }

    @Override
    public R<List<Player>> getPlayerInfoList(HttpServletRequest httpServletRequest) {
        Long creatorId = jwtUtil.parseJwt(httpServletRequest.getHeader("token")).getId();
        LambdaQueryWrapper<Player> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Player::getCreatorId, creatorId).orderByDesc(Player::getCreateDate);
        List<Player> player = playerService.list(queryWrapper);
        return R.success(player);
    }

    @Override
    public SparkChatListener imageUnderStandChat(String question, StringBuilder answer,String UID) throws Exception {
        String hostUrl = "https://spark-api.cn-huabei-1.xf-yun.com/v2.1/image";
        // 构建鉴权url
        String authUrl = getAuthUrl(hostUrl,apiKey,apiSecret,"GET");
        OkHttpClient client = new OkHttpClient.Builder().build();
        // 构建聊天请求
        JSONObject chatRequest = buildImageUnderStandChatRequest(question,UID);
        // 构建websocket请求
        Request request = new Request.Builder().url(authUrl).build();
        // todo
        SparkChatListener sparkChatListener = new SparkChatListener(answer,new ArrayList<>());
        // 发起websocket请求
        WebSocket webSocket = client.newWebSocket(request, sparkChatListener);
        if (chatRequest != null) {
            webSocket.send(chatRequest.toJSONString());
        }
        return sparkChatListener;
    }

    @Override
    public R<String> getBase64Link(MultipartFile file) throws IOException {
        String base64Url = Base64.getEncoder().encodeToString(file.getBytes());
        String UID = UUID.randomUUID().toString().replaceAll("-","");
        // 存入redis
        redisService.set(AiPrefix.BASE64URL,UID,base64Url, 60);
        return R.success(UID);
    }

    @Override
    public R<String> fileUnderStandUpload(MultipartFile file) throws IOException {
        String hostUrl = "https://chatdoc.xfyun.cn/openapi/v1/file/upload";
        System.out.println(file.getOriginalFilename());
        OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
                .readTimeout(20, TimeUnit.SECONDS)
                .writeTimeout(20, TimeUnit.SECONDS)
                .connectTimeout(20, TimeUnit.SECONDS)
                .build();

        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);
        builder.addFormDataPart("file", file.getOriginalFilename(),
                RequestBody.create(MediaType.parse("multipart/form-data"), file.getBytes()));
        builder.addFormDataPart("fileType", "wiki");
        RequestBody body = builder.build();
        long ts = System.currentTimeMillis() / 1000;
        Request request = new Request.Builder()
                .url(hostUrl)
                .post(body)
                .addHeader("appId", appId)
                .addHeader("timestamp", String.valueOf(ts))
                .addHeader("signature", Objects.requireNonNull(getSignature(appId, apiSecret, ts)))
                .build();
        try {
            Response response = okHttpClient.newCall(request).execute();
            if (Objects.equals(response.code(), 200)) {
                String respBody = response.body().string();
                System.out.println(respBody);
                return R.success(JSONUtil.toBean(respBody, UploadResponse.class).getData().getFileId());
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    @Override
    public FileUnderstandListener fileUnderStandChat(String question, StringBuilder answer, String fileId) {
        // 问答开始之前 判断文件状态
        if ( !Objects.equals(getFileStatus(fileId), "vectored") ) {
            System.out.println("文件还没上传好呢");
            return null;
        }
        String hostUrl = "wss://chatdoc.xfyun.cn/openapi/chat";
        RoleContent roleContent = new RoleContent();
        roleContent.setRole("user");
        roleContent.setContent(question);
        // 请求内容
        ChatRequest chatRequest = ChatRequest.builder()
                .fileIds(Collections.singletonList(fileId))
                .topN(3)
                .messages(Collections.singletonList(roleContent))
                .build();
        // 构造url鉴权
        long ts = System.currentTimeMillis() / 1000;
        String signature = getSignature(appId, apiSecret, ts);
        String requestUrl = hostUrl + "?" + "appId=" + appId + "&timestamp=" + ts + "&signature=" + signature;
        OkHttpClient client = new OkHttpClient.Builder().build();
        // 构建websocket请求
        Request request = new Request.Builder().url(requestUrl).build();
        FileUnderstandListener fileUnderstandListener = new FileUnderstandListener(answer);
        // 发起websocket请求
        WebSocket webSocket = client.newWebSocket(request, fileUnderstandListener);
        if (chatRequest != null) {
            webSocket.send(JSONUtil.toJsonStr(chatRequest));
        }
        return fileUnderstandListener;
    }

    @Override
    public R<List<ThemeInfo.Datas>> getAiPPTInfo() throws IOException {
        long timestamp = System.currentTimeMillis()/1000;
        String ts = String.valueOf(timestamp);
        // 获得鉴权信息
        String signature = getSignature(appId, apiSecret, timestamp);
        // 建立链接
        ApiClient client = new ApiClient("https://zwapi.xfyun.cn");
        // 查询PPT模板信息
        String templateResult = client.getTemplateList(appId, ts, signature);
        // 转化为JSON对象
        Gson gson = new Gson();
        ThemeInfo themeInfo = gson.fromJson(templateResult, ThemeInfo.class);
        return R.success(themeInfo.getData());
    }

    @Override
    public void getAiPPTWithoutAnything(String type,String author,String query,Boolean isCardNote,Boolean isCoverImg,String theme,SseEmitter emitter) throws IOException, InterruptedException {
        String appId = "92d4a8a5";
        String secret = "ZWFmY2QxY2VhZmZmNTY0YzJhZTQ1NDdi";
        long timestamp = System.currentTimeMillis()/1000;
        String ts = String.valueOf(timestamp);
        // 获得鉴权信息
        String signature = getSignature(appId, secret, timestamp);
        System.out.println(signature);

        // 建立链接
        ApiClient client = new ApiClient("https://zwapi.xfyun.cn");
        String resp = client.createPPT(appId, ts, signature,type,author,query,isCardNote,isCoverImg,theme);
        CreateResponse response = JSON.parseObject(resp, CreateResponse.class);
        System.out.println(response);
        // 利用sid查询PPT生成进度
        int progress = 0;
        ProgressResponse progressResponse = null;
        while (progress < 100) {
            String progressResult = client.checkProgress(appId, ts, signature, response.getData().getSid());
            progressResponse = JSON.parseObject(progressResult, ProgressResponse.class);
            progress = progressResponse.getData().getProcess();
            emitter.send(progressResult);
            System.out.println(progressResult);

            if (progress < 100) {
                Thread.sleep(5000); // 暂停2秒
            }
        }
        emitter.send("&#92n[DONE]");
    }

    @Override
    public SpeechSynthesisListener aiSpeechSynthesis(String text, StringBuilder answer) throws Exception {
        String hostUrl = "https://tts-api.xfyun.cn/v2/tts";
        String authUrl = getAuthUrl(hostUrl, apiKey, apiSecret,"GET").replace("https://", "wss://");;
        OkHttpClient client = new OkHttpClient.Builder().build();
        // 构造请求体
        String speechSynthesisRequest = buildSpeechSynthesisRequest(text);
        // 构建websocket请求
        Request request = new Request.Builder().url(authUrl).build();
        SpeechSynthesisListener speechSynthesisListener = new SpeechSynthesisListener(answer);
        // 发起websocket请求
        WebSocket webSocket = client.newWebSocket(request, speechSynthesisListener);
        webSocket.send(JSONUtil.toJsonStr(speechSynthesisRequest));
        return speechSynthesisListener;
    }

    @Override
    public R<String> getBase64LinkByUID(String uid) {
        String base64Url = redisService.get(AiPrefix.BASE64URL,uid);
        if ( base64Url != null ) {
            return R.success(base64Url);
        }
        return R.error("获取失败，UID不存在");
    }

    @Override
    public R clearChatHistory(HttpServletRequest request) {
        Long userId = jwtUtil.parseJwt(request.getHeader("token")).getId();
        UserTalkInfo userTalkInfo = new UserTalkInfo(userId, "chat");
        if (userTalkHistoryMap.get(userTalkInfo) != null) {
            userTalkHistoryMap.remove(userTalkInfo);
            return R.success("清除成功");
        }
        return R.success("清楚成功");
    }

    /**
     * 获取文档上传的状态
     * @param fileId
     */
    private String getFileStatus(String fileId) {
        String hostUrl = "https://chatdoc.xfyun.cn/openapi/v1/file/status?fileIds=" + fileId;
        OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
                .readTimeout(20, TimeUnit.SECONDS)
                .writeTimeout(20, TimeUnit.SECONDS)
                .connectTimeout(20, TimeUnit.SECONDS)
                .build();

        long ts = System.currentTimeMillis() / 1000;
        // 传参 fileId
        Request request = new Request.Builder()
                .url(hostUrl)
                .get()
                .addHeader("appId", appId)
                .addHeader("timestamp", String.valueOf(ts))
                .addHeader("signature", Objects.requireNonNull(getSignature(appId, apiSecret, ts)))
                .build();
        try {
            Response response = okHttpClient.newCall(request).execute();
            if (Objects.equals(response.code(), 200)) {
                String respBody = response.body().string();
                return JSONUtil.toBean(respBody, FileStatusResponse.class).getData().get(0).getFileStatus();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    private JSONObject buildCharacterSimulationRequest(String question, String playerId, String agentId,String chatId, String preChatId) {
        try {
            JSONObject requestJson = new JSONObject();

            JSONObject header = new JSONObject();
            header.put("app_id", appId);
            header.put("uid", playerId);
            header.put("agent_id", agentId);

            JSONObject parameter = new JSONObject();
            JSONObject chat = new JSONObject();
            chat.put("chat_id",chatId);
            chat.put("pre_chat_id",preChatId);

            JSONObject payload = new JSONObject();
            JSONObject message = new JSONObject();
            JSONArray text = new JSONArray();
            // 对于第一次开始对话 question为空 此时text应为空数组
            if ( question != null ) {
                RoleContent roleContent = new RoleContent();
                roleContent.setRole("user");
                roleContent.setContent(question);
                text.add(JSON.toJSON(roleContent));
            }
            message.put("text", text);
            payload.put("message", message);

            parameter.put("chat", chat);
            requestJson.put("header", header);
            requestJson.put("parameter", parameter);
            requestJson.put("payload", payload);

            return requestJson;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private JSONObject buildChatRequest(String question,List<RoleContent> talkHistory) {
        try {
            JSONObject requestJson = new JSONObject();

            JSONObject header = new JSONObject();
            header.put("app_id", appId);
            header.put("uid", UUID.randomUUID().toString().substring(0, 10));

            JSONObject parameter = new JSONObject();
            JSONObject chat = new JSONObject();

            chat.put("domain", "generalv2");
            chat.put("temperature", 0.5);
            chat.put("max_tokens", 4096);
            parameter.put("chat", chat);

            JSONObject payload = new JSONObject();
            JSONObject message = new JSONObject();
            JSONArray text = talkHistory.stream().map(JSON::toJSON).collect(Collectors.toCollection(JSONArray::new));

//            RoleContent roleContent = new RoleContent();
//            roleContent.setRole("user");
//            roleContent.setContent(question);
//            roleContent.setContent_type("text");
//            text.add(JSON.toJSON(roleContent));

            message.put("text", text);
            payload.put("message", message);

            requestJson.put("header", header);
            requestJson.put("parameter", parameter);
            requestJson.put("payload", payload);
            System.err.println(requestJson);
            return requestJson;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private JSONObject buildImageUnderStandChatRequest(String question,String UID) {
        try {
            JSONObject requestJson=new JSONObject();

            JSONObject header=new JSONObject();  // header参数
            header.put("app_id",appId);
            header.put("uid",UUID.randomUUID().toString().substring(0, 10));

            JSONObject parameter=new JSONObject(); // parameter参数
            JSONObject chat=new JSONObject();
            chat.put("domain","image");
            chat.put("temperature",0.5);
            chat.put("max_tokens",4096);
            chat.put("auditing","default");
            parameter.put("chat",chat);

            JSONObject payload=new JSONObject(); // payload参数
            JSONObject message=new JSONObject();
            JSONArray text=new JSONArray();

            // 最新问题
            RoleContent roleContent=new RoleContent();
            // 问问提前要先给出图片信息 首先添加图片信息 再添加图片问题
            String base64URL = redisService.get(AiPrefix.BASE64URL,UID);
            roleContent.setRole("user");
            roleContent.setContent(base64URL);
            System.out.println("图片信息："+ roleContent.getContent());
            roleContent.setContent_type("image");
            text.add(JSON.toJSON(roleContent));
            // 添加对图片提出要求的信息
            RoleContent roleContent1=new RoleContent();
            roleContent1.setRole("user");
            roleContent1.setContent(question);
            roleContent1.setContent_type("text");
            text.add(JSON.toJSON(roleContent1));

            message.put("text", text);
            payload.put("message", message);

            requestJson.put("header", header);
            requestJson.put("parameter", parameter);
            requestJson.put("payload", payload);
            System.err.println(requestJson);
            return requestJson;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private String buildSpeechSynthesisRequest(String text) {
        String requestJson = null;//请求参数json串
        try {
            // 注意 aue需要使用lame同时sfl值为1 才能获取能用于mp3的base64内容
            requestJson = "{\n" +
                    "  \"common\": {\n" +
                    "    \"app_id\": \"" + appId + "\"\n" +
                    "  },\n" +
                    "  \"business\": {\n" +
                    "    \"aue\": \"lame\",\n" +
                    "    \"sfl\": 1 ,\n" +
                    "    \"tte\": \"" + TTE + "\",\n" +
                    "    \"ent\": \"intp65\",\n" +
                    "    \"vcn\": \"" + VCN + "\",\n" +
                    "    \"pitch\": 50,\n" +
                    "    \"speed\": 50\n" +
                    "  },\n" +
                    "  \"data\": {\n" +
                    "    \"status\": 2,\n" +
                    "    \"text\": \"" + Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8)) + "\"\n" +
                    "  }\n" +
                    "}";
        }catch (Exception e) {
            e.printStackTrace();
        }
        return requestJson;
    }
}
